package com.zkh.myframe.database.mapper.parser;

import java.util.HashMap;
import java.util.Map;
import com.zkh.myframe.database.mapper.MapperEntity;
import com.zkh.myframe.database.mapper.MapperParseException;
import com.zkh.myframe.database.mapper.SQLEntity;
import com.zkh.myutils.markup.Element;
import com.zkh.myutils.utils.StringUtils;

/**
 * 主解析器
 * @author zkh
 */
public class Parser {
	
	//待处理的临时命名空间和SQL对象存储
	static Map<String, Map<String, SQLEntity>> unDealSQLMap = new HashMap<>();
	
	/**
	 * 解析Mapper.xml文件
	 * @param root 根节点
	 * @param namespace 命名空间
	 */
	public static void renderMapper(Element root, String namespace, MapperEntity mapper) {
		//遍历第一级子节点
		 root.getChildren().forEach(node->{
			//标签名称
			String tagName = node.getName();
			//文本节点，则跳过
			if(node.isText()){
				return;
			}
			//获取ID
			String id = node.getAttr("id");
			String dataSource = node.getAttr("data-source");
			//为空
			if(StringUtils.isEmpty(id)) {
				throw new MapperParseException("标签" + tagName + "的id不能为空");
			}
			//SQL标签
			if("sql".equalsIgnoreCase(tagName)) {
				mapper.addSQL(id, getSQLEntity(node, namespace, id, null, true));
			}else if("select".equalsIgnoreCase(tagName)){
				mapper.addSelect(id, getSQLEntity(node, namespace, id, dataSource, false));
			}else if("insert".equalsIgnoreCase(tagName)) {
				mapper.addInsert(id, getSQLEntity(node, namespace, id, dataSource, false));
			}else if("update".equalsIgnoreCase(tagName)) {
				mapper.addUpdate(id, getSQLEntity(node, namespace, id, dataSource, false));
			}else if("delete".equalsIgnoreCase(tagName)) {
				mapper.addDelete(id, getSQLEntity(node, namespace, id, dataSource, false));
			}
		 });
	}
	
	/**
	 * 检查未处理的SQL
	 */
	public static void checkUnDealSQL() {
		//检查未处理的SQL标签是否处理完
		StringBuffer errors = new StringBuffer();
		//遍历异常
		unDealSQLMap.forEach((k, v)->{
			//遍历ID
			v.forEach((k1, v1) -> errors.append("；include标签引用的").append(v1.getNamespace()).append('.').append(v1.getId()).append(" 不存在"));
		});
		//如果有未处理完的SQL标签，且还在引用中，则抛出异常，如果未引用，则直接忽略
		if(!unDealSQLMap.isEmpty() && errors.length()>1) {
			throw new MapperParseException(errors.substring(1));
		}
	}
	
	/**
	 * 获取具体的SQLEntity对象（一级标签）
	 * @param node SQL标签节点
	 * @param namespace 命名空间
	 * @param id id值
	 * @param isSQLTag 是否是SQL标签
	 */
	private static SQLEntity getSQLEntity(Element node, String namespace, String id, String dataSource, boolean isSQLTag) {
		//创建对象
		SQLEntity sqlEntity = new SQLEntity(namespace, id, dataSource);
		//如果是SQL标签，从未处理的对象中获取对象
		if(isSQLTag) {
			// 获取未处理的SQL
			Map<String, SQLEntity> mapperMap = unDealSQLMap.get(namespace);
			//存在
			if(mapperMap!=null && mapperMap.containsKey(id)) {
				//保存并移除未处理记录
				sqlEntity = mapperMap.remove(id);
			}
		}
		//没有子节点
		if(!node.hasChild()) {
			sqlEntity.appendSQL(node.getText(true).replaceAll("\\s+", " ").trim());
		}else {
			//遍历子节点
			for(Element cnode: node.getChildren()) {
				//标签名称
				String tagName = cnode.getName();
				//判断标签（include，trim，where，foreach，if或文本）
				if(cnode.isText()) {
					sqlEntity.appendSQL(cnode.getText(true).replaceAll("\\s+", " ").trim());
				}else if("include".equalsIgnoreCase(tagName)){
					sqlEntity.appendInclude(IncludeParser.parse(cnode, namespace));
					sqlEntity.appendSQL("{:include}");
				}else if("where".equalsIgnoreCase(tagName)) {
					sqlEntity.appendWhere(WhereParser.parse(cnode, namespace));
					sqlEntity.appendSQL("{:where}");
				}else if("foreach".equalsIgnoreCase(tagName)) {
					sqlEntity.appendForeach(ForeachParser.parse(cnode, namespace));
					sqlEntity.appendSQL("{:foreach}");
				}else if("if".equalsIgnoreCase(tagName)) {
					sqlEntity.appendIF(IfParser.parse(cnode, namespace));
					sqlEntity.appendSQL("{:if}");
				}else if("trim".equalsIgnoreCase(tagName)) {
					sqlEntity.appendTrim(TrimParser.parse(cnode, namespace));
					sqlEntity.appendSQL("{:trim}");
				}
			}
		}
		//返回
		return sqlEntity;
	}
	
	/**
	 * 保存未处理的SQL标签
	 * @param namespace 命名空间
	 * @param id 方法名
	 */
	static void putUnDealSQL(String namespace, String id, SQLEntity sqlEntity) {
		//保存
        unDealSQLMap.computeIfAbsent(namespace, k -> new HashMap<>()).put(id, sqlEntity);
	}
}
